package com.simen.hellobytes.zips;

import com.simen.hellobytes.IOUtil;

import java.io.ByteArrayOutputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;

/**
 * deflater算法解压缩类
 * author: SimenHi
 * create time: 2020-08-28 10:53
 * description:
 */
public class InflaterHelper {

    private byte[] input;
    private int compressBuffLength = 1024;
    private boolean wholeDeCompress = false;
    private int inputBuffLength = 1024;
    private int inputPosition = 0;
    private byte[] data;
    private byte[] decompressData;
    /**
     * 网页解压缩应设置为true
     */
    private boolean nowrap=false;

    public InflaterHelper() {
    }

    public InflaterHelper(byte[] input) {
        this.input = input;
    }

    public InflaterHelper(byte[] input, boolean nowrap) {
        this.input = input;
        this.nowrap = nowrap;
    }

    public InflaterHelper decompress() {
        ByteArrayOutputStream byteBuff =null;
        Inflater decompresser = new Inflater(nowrap);
        try {
            byte[] inputBuff = null;
            int read = 0;

            if (wholeDeCompress) {
                decompresser.setInput(input, 0, input.length);
            } else {
                inputBuff = new byte[inputBuffLength];

                read = readDataToBuff(inputBuff);
                if (read == -1) {
                    throw new DataFormatException("input data is empty");
                }
                decompresser.setInput(inputBuff, 0, read);
            }

            byte[] buff = new byte[compressBuffLength];
            byteBuff = new ByteArrayOutputStream();
            while (!decompresser.finished()) {
                int decompressLength = decompresser.inflate(buff);
                if (decompressLength == 0) {
                    if (wholeDeCompress) {
                        //throw new DataFormatException("input data no more");
                        break;
                    } else {
                        read = readDataToBuff(inputBuff);
                        if (read == -1) {
                            throw new DataFormatException("input data no more");
                        }
                        decompresser.setInput(inputBuff, 0, read);
                    }
                } else {
                    byteBuff.write(buff, 0, decompressLength);
                }
            }
            this.decompressData = byteBuff.toByteArray();
        } catch (DataFormatException e) {
            e.printStackTrace();
        } finally {
            decompresser.end();
            IOUtil.closeSilently(byteBuff);
        }
        return this;
    }

    private int readDataToBuff(byte[] inputBuff) {
        int remainLength = input.length - inputPosition;
        if (remainLength < 1) {
            return -1;
        }

        int willReadLength = Math.min(remainLength, inputBuffLength);
        System.arraycopy(input, inputPosition, inputBuff, 0, willReadLength);
        return willReadLength;
    }

    public byte[] getInput() {
        return input;
    }

    public InflaterHelper setInput(byte[] input) {
        this.input = input;
        return this;
    }

    public int getCompressBuffLength() {
        return compressBuffLength;
    }

    public InflaterHelper setCompressBuffLength(int compressBuffLength) {
        this.compressBuffLength = compressBuffLength;
        return this;
    }

    public boolean isWholeDeCompress() {
        return wholeDeCompress;
    }

    public InflaterHelper setWholeDeCompress(boolean wholeDeCompress) {
        this.wholeDeCompress = wholeDeCompress;
        return this;
    }

    public byte[] getData() {
        return data;
    }

    public InflaterHelper setData(byte[] data) {
        this.data = data;
        return this;
    }

    public int getInputBuffLength() {
        return inputBuffLength;
    }

    public InflaterHelper setInputBuffLength(int inputBuffLength) {
        this.inputBuffLength = inputBuffLength;
        return this;
    }

    public int getInputPosition() {
        return inputPosition;
    }

    public byte[] getDecompressData() {
        return decompressData;
    }

    public int getDeompressDataLength() {
        return decompressData != null ? decompressData.length : -1;
    }

    public boolean isNowrap() {
        return nowrap;
    }

    public InflaterHelper setNowrap(boolean nowrap) {
        this.nowrap = nowrap;
        return this;
    }
}
